////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "ConvexHullCreatorDialog.h"

#include <fstream>
#include <iostream>

#include <SmegLight/Hull/ConvexHull.hpp>
#include <SmegLight/Light/LightSystem.hpp>

////////////////////////////////////////////////////////////
/// Construct the SmegEditorFrame
////////////////////////////////////////////////////////////
ConvexHullCreatorDialog::ConvexHullCreatorDialog( wxWindow* _pParent, smeg::light::ConvexHull* _pConvexHull ) :
    ConvexHullCreatorDialogGUI( _pParent ),
    m_pConvexHull( _pConvexHull ),
    m_Filename( wxEmptyString )
{
    // SFML Canvas
    m_pCanvas = new ConvexHullCreatorCanvas(this, wxID_ANY);
    m_canvasLayout->Add( m_pCanvas, 1, wxEXPAND | wxALL, 0, 0 );

    // Size Multiplier Spin
	m_pSizeMultiplierSpin = new wxSpinCtrlDouble( m_rightPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0.001, 999.0, 1.0, 0.1 );
	m_multiplierLayout->Add( m_pSizeMultiplierSpin, 3, wxALL|wxEXPAND, 5 );

    // PropertyGrid
    m_pPropertyGrid = new wxPropertyGrid(m_propertyPanel, 
        wxID_VERTEX_PROPERTY,
        wxDefaultPosition,
        wxDefaultSize,
        wxPG_AUTO_SORT | // Automatic sorting after items added
        wxPG_SPLITTER_AUTO_CENTER | // Automatically center splitter until user manually adjusts it
        wxPG_DEFAULT_STYLE);

    m_propertyLayout->Add( m_pPropertyGrid, 1, wxEXPAND | wxALL, 0 );

    if ( m_pConvexHull->GetNumVertices() < 3 ) {
        // We have 3 vertices at the beginning
        m_uNumCurrentVertices = 3;
        m_pCanvas->Resize( m_uNumCurrentVertices );

        wxPGProperty
            // Vertex 0
            * pgSet0 = new wxStringProperty("vertex[0]", "0", "<composed>"),
            * pgVert0x = new wxFloatProperty("x", "x", 0.0f),
            * pgVert0y = new wxFloatProperty("y", "y", 100.0f),
            // Vertex 1
            * pgSet1 = new wxStringProperty("vertex[1]", "1", "<composed>"),
            * pgVert1x = new wxFloatProperty("x", "x", -100.0f),
            * pgVert1y = new wxFloatProperty("y", "y", -100.0f),
            // Vertex 2
            * pgSet2 = new wxStringProperty("vertex[2]", "2", "<composed>"),
            * pgVert2x = new wxFloatProperty("x", "x", 100.0f),
            * pgVert2y = new wxFloatProperty("y", "y", -100.0f);
    
        // Vertex 0
        m_pPropertyGrid->Append(pgSet0);
        m_pPropertyGrid->AppendIn(pgSet0, pgVert0x);
        m_pPropertyGrid->AppendIn(pgSet0, pgVert0y);
        m_pCanvas->UpdateVertexPosition( 0, 0.0f, 100.0f );

        // Vertex 1
        m_pPropertyGrid->Append(pgSet1);
        m_pPropertyGrid->AppendIn(pgSet1, pgVert1x);
        m_pPropertyGrid->AppendIn(pgSet1, pgVert1y);
        m_pCanvas->UpdateVertexPosition( 1, -100.0f, -100.0f );

        // Vertex 2
        m_pPropertyGrid->Append(pgSet2);
        m_pPropertyGrid->AppendIn(pgSet2, pgVert2x);
        m_pPropertyGrid->AppendIn(pgSet2, pgVert2y);
        m_pCanvas->UpdateVertexPosition( 2, 100.0f, -100.0f );
    } else {
        m_uNumCurrentVertices = m_pConvexHull->GetNumVertices();
        m_pCanvas->Resize( m_uNumCurrentVertices );

        for (int i = 0; i < m_uNumCurrentVertices; ++i) {
            const sf::Vector2f& vertexPos( m_pConvexHull->GetLocalVertex( i ) );

            wxPGProperty * pgSet = new wxStringProperty(
                    wxString::Format("vertex[%d]", i),
                    wxString::Format("%d", i),
                    "<composed>"),
                * pgVertx = new wxFloatProperty("x", "x", vertexPos.x),
                * pgVerty = new wxFloatProperty("y", "y", vertexPos.y);
    
            m_pPropertyGrid->Append(pgSet);
            m_pPropertyGrid->AppendIn(pgSet, pgVertx);
            m_pPropertyGrid->AppendIn(pgSet, pgVerty);

            m_pCanvas->UpdateVertexPosition( i, vertexPos.x, vertexPos.y );
        }

        m_numVerticesSpin->SetValue( m_uNumCurrentVertices );
    }

    // Connect Events
    this->Connect(wxID_VERTEX_PROPERTY, wxEVT_PG_CHANGED, wxPropertyGridEventHandler(ConvexHullCreatorDialog::OnPropertyChanged));
}

////////////////////////////////////////////////////////////
/// When a vertex was moved in the canvas
////////////////////////////////////////////////////////////
void ConvexHullCreatorDialog::OnVertexMoved( int _iNumVertex, float _x, float _y ) {
    wxPGProperty* vertexProp = m_pPropertyGrid->GetProperty( wxString::Format("%d", _iNumVertex) );
    
    vertexProp->GetPropertyByName("x")->SetValueFromString( wxString::Format("%f", _x) );
    vertexProp->GetPropertyByName("y")->SetValueFromString( wxString::Format("%f", _y) );
}

////////////////////////////////////////////////////////////
/// Save a ConvexHull to a file
////////////////////////////////////////////////////////////
bool ConvexHullCreatorDialog::SaveConvexHull( const wxString& _Filename ) {

    std::ofstream file( _Filename.c_str().AsChar() );
    bool savingSuccess = false;

    if ( file.good() ) {
        const sf::VertexArray& vArray( m_pCanvas->GetVertexArray() );
        smeg::light::ConvexHull convexHullTmp( vArray, m_pSizeMultiplierSpin->GetValue() );

        file << convexHullTmp.GetNumVertices() << std::endl;

        for ( unsigned i = 0; i < convexHullTmp.GetNumVertices(); ++i ) {
            file << convexHullTmp.GetLocalVertex( i ).x << " "
                << convexHullTmp.GetLocalVertex( i ).y << " "
                << std::endl;
        }

        smeg::light::LightSystem::GetInstance()->RemoveHull( &convexHullTmp, false );

        savingSuccess = true;
    }

    file.close();

    return savingSuccess;
}

////////////////////////////////////////////////////////////
/// Event handlers
////////////////////////////////////////////////////////////
void ConvexHullCreatorDialog::OnValidateConvexHull( wxCommandEvent& event ) {
    if ( m_pConvexHull ) {
        const sf::VertexArray& vArray(m_pCanvas->GetVertexArray());

        if ( smeg::light::ConvexHull::IsValidConvexHull( vArray ) ) {
            wxFileDialog* dlg = new wxFileDialog(this, 
                                                    _T("Save your ConvexHull..."),
                                                    wxEmptyString, wxEmptyString, 
                                                    "CVX Files (*.cvx)|*.cvx",
                                                    wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
            
            if ( wxID_CANCEL != dlg->ShowModal() ) {
                wxString fileName = dlg->GetPath();
                if ( SaveConvexHull( dlg->GetPath() ) ) {
                    m_pConvexHull->SetupShape( m_pCanvas->GetVertexArray(), m_pSizeMultiplierSpin->GetValue() );
                    m_Filename = fileName;
                    Close(true);
                } else {
	                wxMessageBox(_T("The ConvexHull you created couldn't be saved!\n"),
	                                _T("ConvexHull Creator Error"),
	                                wxOK | wxICON_ERROR,
	                                this);
                }
            }

            dlg->Destroy();
        } else {
	        wxMessageBox(_T("The Hull you created is not Convex!\n"),
	                        _T("ConvexHull Creator Error"),
	                        wxOK | wxICON_ERROR,
	                        this);
        }
    } else {
        Close(true);
    }
}
void ConvexHullCreatorDialog::OnCancelConvexHull( wxCommandEvent& event ) {
    wxMessageDialog* dlg = new wxMessageDialog(this, 
                                        _T("Are you sure you want to cancel your modifications ?"),
                                        _T("ConvexHull Creator"),
                                        wxYES_NO | wxICON_QUESTION);
    int ret = dlg->ShowModal();
    dlg->Destroy();

    if ( wxID_YES == ret ) {
        Close(true);
    }
}

void ConvexHullCreatorDialog::OnNumVerticesChanged( wxSpinEvent& event ) {
    int newNumVertices = event.GetValue();
    m_pCanvas->Resize( newNumVertices );

    if ( newNumVertices > m_uNumCurrentVertices ) {
        int numChanged = newNumVertices - m_uNumCurrentVertices;

        for (int i = 0; i < numChanged; ++i) {
            wxPGProperty * pgSet = new wxStringProperty(
                    wxString::Format("vertex[%d]", m_uNumCurrentVertices + i),
                    wxString::Format("%d", m_uNumCurrentVertices + i),
                    "<composed>"),
                * pgVertx = new wxFloatProperty("x", "x", 0.0f),
                * pgVerty = new wxFloatProperty("y", "y", 0.0f);
    
            m_pPropertyGrid->Append(pgSet);
            m_pPropertyGrid->AppendIn(pgSet, pgVertx);
            m_pPropertyGrid->AppendIn(pgSet, pgVerty);
        }
    } else if ( newNumVertices < m_uNumCurrentVertices ) {
        int numChanged = m_uNumCurrentVertices - newNumVertices;

        for (int i = 0; i < numChanged; ++i) {
            m_pPropertyGrid->DeleteProperty(wxString::Format("%d", newNumVertices + i));
        }
    }

    m_uNumCurrentVertices = newNumVertices;
}

void ConvexHullCreatorDialog::OnPropertyChanged( wxPropertyGridEvent& event ) {
    wxPGProperty* prop = event.GetProperty();
    wxString name = prop->GetName(),
        vertName;
    wxStringTokenizer tkz(name, wxT("."));

    vertName = tkz.GetNextToken();
    long numVert;
    if (vertName.ToLong(&numVert)) {
        wxPGProperty* vertexProp = m_pPropertyGrid->GetProperty(vertName);
        m_pCanvas->UpdateVertexPosition(
            (unsigned)numVert,
            vertexProp->GetPropertyByName("x")->GetValue().GetAny().As<float>(),
            vertexProp->GetPropertyByName("y")->GetValue().GetAny().As<float>()
            );
    }
}